
#include <promote/StopWatch.hpp>
#include <promote/Timestamp.hpp>
#include <promote/TimeUtils.hpp>

#include "BinomialTree.hpp"
#include "BinomialTreeTest.hpp"
#include "ExpiryCalculator.hpp"
#include "VanillaOption.hpp"

using namespace provident;
using namespace provident::test;

template <typename FACTOR_MODEL>
static double perfTestAmericanCall(int const height);
template <typename FACTOR_MODEL>
static double perfTestEuropeanCall(int const height);
template <typename FACTOR_MODEL>
static double perfTestAmericanPut(int const height);
template <typename FACTOR_MODEL>
static double perfTestEuropeanPut(int const height);

void provident::test::unitTestBinomialTree()
{
}

void provident::test::perfTestBinomialTree()
{
    for(int ii = 25; ii <= 200; ii += 25) {
        printf("Cox/Ross/Rubinstein american call (height=%d): %g ns\n", ii, 
               perfTestAmericanCall<CoxRossRubinstein>(ii));
        printf("Equal Probabilities american call (height=%d): %g ns\n", ii, 
               perfTestAmericanCall<EqualProbabilities>(ii));
        printf("Trigeorgis american call (height=%d): %g ns\n", ii, 
               perfTestAmericanCall<Trigeorgis>(ii));
    }
    for(int ii = 25; ii <= 200; ii += 25) {
        printf("Cox/Ross/Rubinstein european call (height=%d): %g ns\n", ii, 
                perfTestEuropeanCall<CoxRossRubinstein>(ii));
        printf("Equal Probabilities european call (height=%d): %g ns\n", ii, 
               perfTestEuropeanCall<EqualProbabilities>(ii));
        printf("Trigeorgis european call (height=%d): %g ns\n", ii, 
               perfTestEuropeanCall<Trigeorgis>(ii));
    }
    for(int ii = 25; ii <= 200; ii += 25) {
        printf("Cox/Ross/Rubinstein american put (height=%d): %g ns\n", ii, 
               perfTestAmericanPut<CoxRossRubinstein>(ii));
        printf("Equal Probabilities american put (height=%d): %g ns\n", ii, 
               perfTestAmericanPut<EqualProbabilities>(ii));
        printf("Trigeorgis american put (height=%d): %g ns\n", ii, 
               perfTestAmericanPut<Trigeorgis>(ii));
    }
    for(int ii = 25; ii <= 200; ii += 25) {
        printf("Cox/Ross/Rubinstein european put (height=%d): %g ns\n", ii, 
                perfTestEuropeanPut<CoxRossRubinstein>(ii));
        printf("Equal Probabilities european put (height=%d): %g ns\n", ii, 
               perfTestEuropeanCall<EqualProbabilities>(ii));
        printf("Trigeorgis european put (height=%d): %g ns\n", ii, 
               perfTestEuropeanCall<Trigeorgis>(ii));
    }
}

template <typename FACTOR_MODEL>
static double perfTestAmericanCall(int const height)
{
    std::size_t iters(1000000 / height);
    promote::StopWatch stopWatch;
    promote::Timestamp timestamp;
    timestamp.dayOfMonth(150);
    ExpiryCalculator calc(ExpiryCalculator::CALENDAR_YEAR);
    VanillaOption option(promote::String("AAPL OPTION NAME",false),
                         promote::String("AAPL",false),
                         calc.yearsTilExpiry(timestamp), 
                         225, VanillaOption::CALL,
                         VanillaOption::AMERICAN);
    BinomialTree<FACTOR_MODEL> binomial;
    typename BinomialTree<FACTOR_MODEL>::Evaluation eval;
                         
    for(std::size_t iter = 0; iter != iters; ++iter) {
        stopWatch.start();
        binomial.valuate(eval, option, 225.0, 0.5, 0.0025, height); 
        stopWatch.stop();
    }

    return (promote::getTimevalAsMicros(stopWatch.elapsed())) * 1000.0 / iters;
}

template <typename FACTOR_MODEL>
static double perfTestEuropeanCall(int const height)
{
    std::size_t const iters(1000000 / height);
    promote::StopWatch stopWatch;
    promote::Timestamp timestamp;
    timestamp.dayOfMonth(150);
    ExpiryCalculator calc(ExpiryCalculator::CALENDAR_YEAR);
    VanillaOption option(promote::String("AAPL OPTION NAME",false),
                         promote::String("AAPL",false),
                         calc.yearsTilExpiry(timestamp), 
                         225, VanillaOption::CALL,
                         VanillaOption::EUROPEAN);
    BinomialTree<FACTOR_MODEL> binomial;
    typename BinomialTree<FACTOR_MODEL>::Evaluation eval;
                         
    stopWatch.start();
    for(std::size_t iter = 0; iter != iters; ++iter) {
        binomial.valuate(eval, option, 225.0, 0.5, 0.0025, height); 
    }
    stopWatch.stop();

    return (promote::getTimevalAsMicros(stopWatch.elapsed())) * 1000.0 / iters;
}

template <typename FACTOR_MODEL>
static double perfTestAmericanPut(int const height)
{
    std::size_t iters(1000000 / height);
    promote::StopWatch stopWatch;
    promote::Timestamp timestamp;
    timestamp.dayOfMonth(150);
    ExpiryCalculator calc(ExpiryCalculator::CALENDAR_YEAR);
    VanillaOption option(promote::String("AAPL OPTION NAME",false),
                         promote::String("AAPL",false),
                         calc.yearsTilExpiry(timestamp), 
                         225, VanillaOption::PUT,
                         VanillaOption::AMERICAN);
    BinomialTree<FACTOR_MODEL> binomial;
    typename BinomialTree<FACTOR_MODEL>::Evaluation eval;
                         
    for(std::size_t iter = 0; iter != iters; ++iter) {
        stopWatch.start();
        binomial.valuate(eval, option, 225.0, 0.5, 0.0025, height); 
        stopWatch.stop();
    }

    return (promote::getTimevalAsMicros(stopWatch.elapsed())) * 1000.0 / iters;
}

template <typename FACTOR_MODEL>
static double perfTestEuropeanPut(int const height)
{
    std::size_t const iters(1000000 / height);
    promote::StopWatch stopWatch;
    promote::Timestamp timestamp;
    timestamp.dayOfMonth(150);
    ExpiryCalculator calc(ExpiryCalculator::CALENDAR_YEAR);
    VanillaOption option(promote::String("AAPL OPTION NAME",false),
                         promote::String("AAPL",false),
                         calc.yearsTilExpiry(timestamp), 
                         225, VanillaOption::CALL,
                         VanillaOption::EUROPEAN);
    BinomialTree<FACTOR_MODEL> binomial;
    typename BinomialTree<FACTOR_MODEL>::Evaluation eval;
                         
    stopWatch.start();
    for(std::size_t iter = 0; iter != iters; ++iter) {
        binomial.valuate(eval, option, 225.0, 0.5, 0.0025, height); 
    }
    stopWatch.stop();

    return (promote::getTimevalAsMicros(stopWatch.elapsed())) * 1000.0 / iters;
}


